Reactã®createPortal APIã«é¢ããå æ¬çãªã¬ã€ããããŒã¿ã«äœææè¡ãã€ãã³ããã³ããªã³ã°æŠç¥ãæè»ã§ã¢ã¯ã»ã·ãã«ãªUIãæ§ç¯ããããã®é«åºŠãªãŠãŒã¹ã±ãŒã¹ã解説ããŸãã
React createPortal: ããŒã¿ã«äœæãšã€ãã³ããã³ããªã³ã°ããã¹ã¿ãŒãã
ReactãçšããçŸä»£ã®Webéçºã§ã¯ãåºç€ãšãªãããã¥ã¡ã³ãæ§é ãšã·ãŒã ã¬ã¹ã«çµ±åããããŠãŒã¶ãŒã€ã³ã¿ãŒãã§ãŒã¹ãäœæããããšãäžå¯æ¬ ã§ããReactã®ã³ã³ããŒãã³ãã¢ãã«ã¯ä»®æ³DOMã®ç®¡çã«åªããŠããŸãããæã«ã¯éåžžã®ã³ã³ããŒãã³ãéå±€ã®å€ã«èŠçŽ ãã¬ã³ããªã³ã°ããå¿
èŠããããŸããããã§ç»å Žããã®ãcreatePortalã§ãããã®ã¬ã€ãã§ã¯ãcreatePortalãæ·±ãæãäžãããã®ç®çãäœ¿çšæ¹æ³ãã€ãã³ããã³ããªã³ã°ãè€éãªUIèŠçŽ ãæ§ç¯ããããã®é«åºŠãªãã¯ããã¯ã«ã€ããŠè§£èª¬ããŸããåœéåã«é¢ããèæ
®äºé
ãã¢ã¯ã»ã·ããªãã£ã®ãã¹ããã©ã¯ãã£ã¹ãé¿ããã¹ãäžè¬çãªèœãšã穎ã«ã€ããŠãè§ŠããŠãããŸãã
React createPortalãšã¯ïŒ
createPortalã¯ãReactã³ã³ããŒãã³ãã®åèŠçŽ ãã芪ã³ã³ããŒãã³ãã®éå±€ã®å€ã«ããDOMããªãŒã®å¥ã®éšåã«ã¬ã³ããªã³ã°ã§ããããã«ããReact APIã§ããããã¯ãã¢ãŒãã«ãããŒã«ããããããããããŠã³ããªãŒããŒã¬ã€ã®ãããªèŠçŽ ãäœæããéã«ç¹ã«äŸ¿å©ã§ãããããã®èŠçŽ ã¯ãããããããªã¬ãŒããã³ã³ããŒãã³ããReactã³ã³ããŒãã³ãããªãŒã®ã©ãã«äœçœ®ããŠãããã«é¢ããããããã¥ã¡ã³ãã®ãããã¬ãã«ãç¹å®ã®ã³ã³ããå
ã«é
眮ããå¿
èŠããããŸãã
createPortalãªãã§ãããå®çŸããã«ã¯ãDOMãçŽæ¥æäœããããCSSã®çµ¶å¯Ÿé
眮ã䜿çšããããããªã©ãè€éãªåé¿çãå¿
èŠã«ãªãããšãå€ããã¹ã¿ããã³ã°ã³ã³ããã¹ããz-indexã®ç«¶åãã¢ã¯ã»ã·ããªãã£ã®åé¡ã«ã€ãªããå¯èœæ§ããããŸãã
ãªãcreatePortalã䜿çšããã®ãïŒ
createPortalãããªãã®ReactããŒã«ãããã®äžã§äŸ¡å€ããããŒã«ã§ããäž»ãªçç±ã¯ä»¥äžã®éãã§ãïŒ
- DOMæ§é ã®æ¹åïŒ ã³ã³ããŒãã³ããDOMå ã§æ·±ããã¹ãããã®ãé¿ããããã¯ãªãŒã³ã§ç®¡çããããæ§é ãå®çŸããŸããããã¯ãå€ãã®ã€ã³ã¿ã©ã¯ãã£ããªèŠçŽ ãæã€è€éãªã¢ããªã±ãŒã·ã§ã³ã«ãšã£ãŠç¹ã«éèŠã§ãã
- ã¹ã¿ã€ãªã³ã°ã®ç°¡çŽ åïŒ è€éãªCSSã®ããªãã¯ã«é Œãããšãªãããã¥ãŒããŒããç¹å®ã®ã³ã³ããã«å¯ŸããŠèŠçŽ ãç°¡åã«é 眮ã§ããŸããããã«ãããç¹ã«ä»ã®ã³ã³ãã³ãã®äžã«ãªãŒããŒã¬ã€ããå¿ èŠãããèŠçŽ ãæ±ãéã®ã¹ã¿ã€ãªã³ã°ãšã¬ã€ã¢ãŠããç°¡çŽ åãããŸãã
- ã¢ã¯ã»ã·ããªãã£ã®åäžïŒ ã³ã³ããŒãã³ãéå±€ããç¬ç«ããŠãã©ãŒã«ã¹ãããŒããŒãããã²ãŒã·ã§ã³ã管çã§ããããã«ããããšã§ãã¢ã¯ã»ã·ãã«ãªUIã®äœæã容æã«ããŸããäŸãã°ãã¢ãŒãã«ãŠã£ã³ããŠå ã«ãã©ãŒã«ã¹ã確å®ã«çããããšãã§ããŸãã
- ããè¯ãã€ãã³ããã³ããªã³ã°ïŒ ããŒã¿ã«ã®ã³ã³ãã³ãããReactããªãŒãžãšã€ãã³ããæ£ããäŒæïŒãããªã³ã°ïŒããããšãå¯èœã«ãã芪ã³ã³ããŒãã³ãã«ã¢ã¿ãããããã€ãã³ããªã¹ããŒãæåŸ ã©ããã«åäœããããšãä¿èšŒããŸãã
createPortalã®åºæ¬çãªäœ¿ãæ¹
createPortal APIã¯2ã€ã®åŒæ°ãåãåããŸãïŒ
- ã¬ã³ããªã³ã°ãããReactããŒãïŒJSXïŒã
- ããŒããã¬ã³ããªã³ã°ãããDOMèŠçŽ ããã®DOMèŠçŽ ã¯ã
createPortalã䜿çšããã³ã³ããŒãã³ããããŠã³ããããåã«ååšããŠããããšãçæ³çã§ãã
以äžã«ç°¡åãªäŸã瀺ããŸãïŒ
äŸïŒã¢ãŒãã«ã®ã¬ã³ããªã³ã°
bodyèŠçŽ ã®æåŸã«ã¬ã³ããªã³ã°ãããã¢ãŒãã«ã³ã³ããŒãã³ãããããšããŸãã
import React from 'react';
import ReactDOM from 'react-dom';
function Modal({ children, isOpen, onClose }) {
if (!isOpen) return null;
const modalRoot = document.getElementById('modal-root'); // Assumes you have a <div id="modal-root"></div> in your HTML
if (!modalRoot) {
console.error('Modal root element not found!');
return null;
}
return ReactDOM.createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
</div>
</div>,
modalRoot
);
}
export default Modal;
解説ïŒ
createPortalã¯ReactDOMãªããžã§ã¯ãã®ã¡ãœããã§ãããããReactDOMãã€ã³ããŒãããŸãã- HTMLå
ã«IDã
modal-rootã®DOMèŠçŽ ãååšãããšä»®å®ããŸããããã«ã¢ãŒãã«ãã¬ã³ããªã³ã°ãããŸãããã®èŠçŽ ãååšããããšã確èªããŠãã ãããäžè¬çãªæ £ç¿ãšããŠãindex.htmlãã¡ã€ã«ã®éãã</body>ã¿ã°ã®çŽåã«<div id="modal-root"></div>ã远å ããŸãã ReactDOM.createPortalã䜿çšããŠãã¢ãŒãã«ã®JSXãmodalRootèŠçŽ ã«ã¬ã³ããªã³ã°ããŸããe.stopPropagation()ã䜿çšããŠãã¢ãŒãã«ã³ã³ãã³ãã®onClickã€ãã³ãããªãŒããŒã¬ã€ã®onCloseãã³ãã©ãããªã¬ãŒããã®ãé²ããŸããããã«ãããã¢ãŒãã«å ãã¯ãªãã¯ããŠãã¢ãŒãã«ãéããªãããã«ããŸãã
äœ¿çšæ¹æ³ïŒ
import React, { useState } from 'react';
import Modal from './Modal';
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<h2>Modal Content</h2>
<p>This is the content of the modal.</p>
<button onClick={() => setIsModalOpen(false)}>Close</button>
</Modal>
</div>
);
}
export default App;
ãã®äŸã¯ãéåžžã®ã³ã³ããŒãã³ãéå±€ã®å€ã«ã¢ãŒãã«ãã¬ã³ããªã³ã°ããæ¹æ³ã瀺ããŠãããããŒãžäžã§çµ¶å¯Ÿçã«é
眮ããããšãã§ããŸãããã®ããã«createPortalã䜿çšããããšã§ãã¹ã¿ããã³ã°ã³ã³ããã¹ãã«é¢ããäžè¬çãªåé¡ã解決ããã¢ããªã±ãŒã·ã§ã³å
šäœã§äžè²«ããã¢ãŒãã«ã¹ã¿ã€ãªã³ã°ãç°¡åã«äœæã§ããŸãã
createPortalã§ã®ã€ãã³ããã³ããªã³ã°
createPortalã®äž»èŠãªå©ç¹ã®1ã€ã¯ãReactã®éåžžã®ã€ãã³ããããªã³ã°åäœãç¶æããããšã§ããããã¯ãããŒã¿ã«ã®ã³ã³ãã³ãå
ã§çºçããã€ãã³ããReactã³ã³ããŒãã³ãããªãŒãäžã£ãŠäŒæãã芪ã³ã³ããŒãã³ããããããåŠçã§ããããšãæå³ããŸãã
ããããã€ãã³ããããŒã¿ã«ã®å¢çãè¶ããéã«ã©ã®ããã«åŠçãããããçè§£ããããšãéèŠã§ãã
äŸïŒããŒã¿ã«å€ã®ã€ãã³ããåŠçãã
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
function OutsideClickExample() {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const portalRoot = document.getElementById('portal-root');
useEffect(() => {
function handleClickOutside(event) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [dropdownRef]);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Dropdown</button>
{isOpen && portalRoot && ReactDOM.createPortal(
<div ref={dropdownRef} style={{ position: 'absolute', top: '50px', left: '0', border: '1px solid black', padding: '10px', backgroundColor: 'white' }}>
Dropdown Content
</div>,
portalRoot
)}
</div>
);
}
export default OutsideClickExample;
解説ïŒ
- ããŒã¿ã«å
ã«ã¬ã³ããªã³ã°ãããããããããŠã³èŠçŽ ã«ã¢ã¯ã»ã¹ããããã«
refã䜿çšããŸãã - ããããããŠã³ã®å€åŽã§ã®ã¯ãªãã¯ãæ€åºããããã«ã
documentã«mousedownã€ãã³ããªã¹ããŒãã¢ã¿ããããŸãã - ã€ãã³ããªã¹ããŒå
ã§ã
dropdownRef.current.contains(event.target)ã䜿çšããŠãã¯ãªãã¯ãããããããŠã³ã®å€åŽã§çºçãããã©ããã確èªããŸãã - ã¯ãªãã¯ãããããããŠã³ã®å€åŽã§çºçããå Žåã
isOpenãfalseã«èšå®ããŠéããŸãã
ãã®äŸã¯ãããŒã¿ã«ã®ã³ã³ãã³ãã®å€ã§çºçããã€ãã³ããåŠçããæ¹æ³ã瀺ããŠãããåšå²ã®ããã¥ã¡ã³ãã§ã®ãŠãŒã¶ãŒã¢ã¯ã·ã§ã³ã«å¿çããã€ã³ã¿ã©ã¯ãã£ããªèŠçŽ ãäœæã§ããŸãã
é«åºŠãªãŠãŒã¹ã±ãŒã¹
createPortalã¯åçŽãªã¢ãŒãã«ãããŒã«ãããã«éå®ãããŸããã以äžã®ãããªæ§ã
ãªé«åºŠãªã·ããªãªã§äœ¿çšã§ããŸãïŒ
- ã³ã³ããã¹ãã¡ãã¥ãŒïŒ å³ã¯ãªãã¯æã«ããŠã¹ã«ãŒãœã«ã®è¿ãã«ã³ã³ããã¹ãã¡ãã¥ãŒãåçã«ã¬ã³ããªã³ã°ããŸãã
- éç¥ïŒ ã³ã³ããŒãã³ãéå±€ã«é¢ä¿ãªããç»é¢äžéšã«éç¥ã衚瀺ããŸãã
- ã«ã¹ã¿ã ããããªãŒããŒïŒ é«åºŠãªé 眮ãšã¹ã¿ã€ãªã³ã°ãæã€ã«ã¹ã¿ã ããããªãŒããŒã³ã³ããŒãã³ããäœæããŸãã
- ãµãŒãããŒãã£ã©ã€ãã©ãªãšã®çµ±åïŒ ç¹å®ã®DOMæ§é ãå¿
èŠãšãããµãŒãããŒãã£ã©ã€ãã©ãªãšReactã³ã³ããŒãã³ããçµ±åããããã«
createPortalã䜿çšããŸãã
äŸïŒã³ã³ããã¹ãã¡ãã¥ãŒã®äœæ
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
function ContextMenuExample() {
const [contextMenu, setContextMenu] = useState(null);
const menuRef = useRef(null);
useEffect(() => {
function handleClickOutside(event) {
if (menuRef.current && !menuRef.current.contains(event.target)) {
setContextMenu(null);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [menuRef]);
const handleContextMenu = (event) => {
event.preventDefault();
setContextMenu({
x: event.clientX,
y: event.clientY,
});
};
const portalRoot = document.getElementById('portal-root');
return (
<div onContextMenu={handleContextMenu} style={{ border: '1px solid black', padding: '20px' }}>
Right-click here to open context menu
{contextMenu && portalRoot && ReactDOM.createPortal(
<div
ref={menuRef}
style={{
position: 'absolute',
top: contextMenu.y,
left: contextMenu.x,
border: '1px solid black',
padding: '10px',
backgroundColor: 'white',
}}
>
<ul>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
</div>,
portalRoot
)}
</div>
);
}
export default ContextMenuExample;
解説ïŒ
- ã¿ãŒã²ããèŠçŽ ã§ã®å³ã¯ãªãã¯ãæ€åºããããã«
onContextMenuã€ãã³ãã䜿çšããŸãã event.preventDefault()ã䜿çšããŠãããã©ã«ãã®ã³ã³ããã¹ãã¡ãã¥ãŒã衚瀺ãããã®ãé²ããŸãã- ããŠã¹ã®åº§æšã
contextMenuã¹ããŒã倿°ã«ä¿åããŸãã - ããŒã¿ã«å ã«ãããŠã¹ã®åº§æšã«é 眮ãããã³ã³ããã¹ãã¡ãã¥ãŒãã¬ã³ããªã³ã°ããŸãã
- ãŠãŒã¶ãŒãã³ã³ããã¹ãã¡ãã¥ãŒã®å€åŽãã¯ãªãã¯ãããšãã«ãããéãããããåã®äŸãšåãå€åŽã¯ãªãã¯æ€åºããžãã¯ãå«ããŸãã
ã¢ã¯ã»ã·ããªãã£ã«é¢ããèæ ®äºé
createPortalã䜿çšããéã¯ãã¢ããªã±ãŒã·ã§ã³ã誰ã«ã§ã䜿ããããã«ãã¢ã¯ã»ã·ããªãã£ãèæ
®ããããšãäžå¯æ¬ ã§ãã
ãã©ãŒã«ã¹ç®¡ç
ããŒã¿ã«ïŒäŸïŒã¢ãŒãã«ïŒãéããšãããã©ãŒã«ã¹ãèªåçã«ããŒã¿ã«å ã®æåã®ã€ã³ã¿ã©ã¯ãã£ããªèŠçŽ ã«ç§»åããããã«ãã¹ãã§ããããã«ãããããŒããŒããã¹ã¯ãªãŒã³ãªãŒããŒã§ããã²ãŒããããŠãŒã¶ãŒãããŒã¿ã«ã®ã³ã³ãã³ãã«ç°¡åã«ã¢ã¯ã»ã¹ã§ããããã«ãªããŸãã
ããŒã¿ã«ãéãããšãã¯ãããŒã¿ã«ãéããã£ãããšãªã£ãèŠçŽ ã«ãã©ãŒã«ã¹ãæ»ãã¹ãã§ããããã«ãããäžè²«ããããã²ãŒã·ã§ã³ãããŒãç¶æãããŸãã
ARIA屿§
ARIA屿§ã䜿çšããŠãããŒã¿ã«ã®ã³ã³ãã³ãã«é¢ããã»ãã³ãã£ãã¯ãªæ
å ±ãæäŸããŸããäŸãã°ãã¢ãŒãã«èŠçŽ ã«aria-modal="true"ã䜿çšããŠããããã¢ãŒãã«ãã€ã¢ãã°ã§ããããšã瀺ããŸããaria-labelledbyã䜿çšããŠã¢ãŒãã«ããã®ã¿ã€ãã«ã«é¢é£ä»ããaria-describedbyã䜿çšããŠãã®èª¬æã«é¢é£ä»ããŸãã
ããŒããŒãããã²ãŒã·ã§ã³
ãŠãŒã¶ãŒãããŒããŒãã䜿çšããŠããŒã¿ã«ã®ã³ã³ãã³ããããã²ãŒãã§ããããšã確èªããŠãã ãããtabindex屿§ã䜿çšããŠãã©ãŒã«ã¹é åºãå¶åŸ¡ãããã¹ãŠã®ã€ã³ã¿ã©ã¯ãã£ããªèŠçŽ ãããŒããŒãã§å°éå¯èœã§ããããšãä¿èšŒããŸãã
ãŠãŒã¶ãŒã誀ã£ãŠããŒã¿ã«ã®å€ã«ããã²ãŒãã§ããªãããã«ãããŒã¿ã«å
ã«ãã©ãŒã«ã¹ããã©ããããããšãæ€èšããŠãã ãããããã¯ãTabããŒããªãã¹ã³ããããã°ã©ã ã§ãã©ãŒã«ã¹ãããŒã¿ã«å
ã®æåãŸãã¯æåŸã®ã€ã³ã¿ã©ã¯ãã£ããªèŠçŽ ã«ç§»åãããããšã§å®çŸã§ããŸãã
äŸïŒã¢ã¯ã»ã·ãã«ãªã¢ãŒãã«
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
function AccessibleModal({ children, isOpen, onClose, labelledBy, describedBy }) {
const modalRef = useRef(null);
const firstFocusableElementRef = useRef(null);
const [previouslyFocusedElement, setPreviouslyFocusedElement] = useState(null);
const modalRoot = document.getElementById('modal-root');
useEffect(() => {
if (isOpen) {
// Save the currently focused element before opening the modal.
setPreviouslyFocusedElement(document.activeElement);
// Focus the first focusable element in the modal.
if (firstFocusableElementRef.current) {
firstFocusableElementRef.current.focus();
}
// Trap focus within the modal.
function handleKeyDown(event) {
if (event.key === 'Tab') {
const focusableElements = modalRef.current.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusableElement = focusableElements[0];
const lastFocusableElement = focusableElements[focusableElements.length - 1];
if (event.shiftKey) {
// Shift + Tab
if (document.activeElement === firstFocusableElement) {
lastFocusableElement.focus();
event.preventDefault();
}
} else {
// Tab
if (document.activeElement === lastFocusableElement) {
firstFocusableElement.focus();
event.preventDefault();
}
}
}
}
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
// Restore focus to the element that had focus before opening the modal.
if(previouslyFocusedElement && previouslyFocusedElement.focus) {
previouslyFocusedElement.focus();
}
};
}
}, [isOpen, previouslyFocusedElement]);
if (!isOpen) return null;
return ReactDOM.createPortal(
<div
className="modal-overlay"
onClick={onClose}
aria-modal="true"
aria-labelledby={labelledBy}
aria-describedby={describedBy}
ref={modalRef}
>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<h2 id={labelledBy}>Modal Title</h2>
<p id={describedBy}>This is the modal content.</p>
<button ref={firstFocusableElementRef} onClick={onClose}>
Close
</button>
{children}
</div>
</div>,
modalRoot
);
}
export default AccessibleModal;
解説ïŒ
aria-modalãaria-labelledbyãaria-describedbyã®ãããªARIA屿§ã䜿çšããŠãã¢ãŒãã«ã«é¢ããã»ãã³ãã£ãã¯ãªæ å ±ãæäŸããŸãã- ã¢ãŒãã«ãééãããšãã®ãã©ãŒã«ã¹ã管çããããã«
useEffectããã¯ã䜿çšããŸãã - ã¢ãŒãã«ãéãåã«çŸåšãã©ãŒã«ã¹ãããŠããèŠçŽ ãä¿åããã¢ãŒãã«ãéãããšãã«ããã«ãã©ãŒã«ã¹ãæ»ããŸãã
keydownã€ãã³ããªã¹ããŒã䜿çšããŠãã¢ãŒãã«å ã«ãã©ãŒã«ã¹ããã©ããããŸãã
åœéåïŒi18nïŒã«é¢ããèæ ®äºé
ã°ããŒãã«ãªãªãŒãã£ãšã³ã¹åãã®ã¢ããªã±ãŒã·ã§ã³ãéçºããéãåœéåïŒi18nïŒã¯éèŠãªèæ
®äºé
ã§ããcreatePortalã䜿çšããéã«ã¯ãããã€ãã®ç¹ã«çæããå¿
èŠããããŸãïŒ
- ããã¹ãã®æ¹åïŒRTL/LTRïŒïŒ ã¹ã¿ã€ãªã³ã°ãå·Šããå³ïŒLTRïŒãšå³ããå·ŠïŒRTLïŒã®äž¡æ¹ã®èšèªã«å¯Ÿå¿ããŠããããšã確èªããŠãã ãããããã«ã¯ãCSSã§è«çããããã£ïŒäŸïŒ
margin-inline-startã®ä»£ããã«margin-leftïŒã䜿çšããããHTMLèŠçŽ ã«dir屿§ãé©åã«èšå®ãããããããšãå«ãŸããå ŽåããããŸãã - ã³ã³ãã³ãã®ããŒã«ã©ã€ãºïŒ ããŒã¿ã«å
ã®ãã¹ãŠã®ããã¹ãã¯ããŠãŒã¶ãŒã®åªå
èšèªã«ããŒã«ã©ã€ãºãããã¹ãã§ããi18nã©ã€ãã©ãªïŒäŸïŒ
react-intlãi18nextïŒã䜿çšããŠç¿»èš³ã管çããŸãã - æ°å€ãšæ¥ä»ã®ãã©ãŒãããïŒ ãŠãŒã¶ãŒã®ãã±ãŒã«ã«åŸã£ãŠæ°å€ãšæ¥ä»ããã©ãŒãããããŸãã
IntlAPIããã®ããã®æ©èœãæäŸããŸãã - æåçæ £ç¿ïŒ UIèŠçŽ ã«é¢é£ããæåçæ £ç¿ã«æ³šæããŠãã ãããäŸãã°ããã¿ã³ã®é çœ®ã¯æåã«ãã£ãŠç°ãªãå ŽåããããŸãã
äŸïŒreact-intlã«ããi18n
import React from 'react';
import { FormattedMessage } from 'react-intl';
function MyComponent() {
return (
<div>
<FormattedMessage id="myComponent.greeting" defaultMessage="Hello, world!" />
</div>
);
}
export default MyComponent;
react-intlã®FormattedMessageã³ã³ããŒãã³ãã¯ããŠãŒã¶ãŒã®ãã±ãŒã«ã«åºã¥ããŠç¿»èš³ãããã¡ãã»ãŒãžãååŸããŸããç°ãªãèšèªã®ç¿»èš³ã§react-intlãèšå®ããŠãã ããã
ããããèœãšã穎ãšè§£æ±ºç
createPortalã¯åŒ·åãªããŒã«ã§ãããããã€ãã®äžè¬çãªèœãšã穎ãšããããåé¿ããæ¹æ³ãèªèããŠããããšãéèŠã§ãïŒ
- ããŒã¿ã«ã«ãŒãèŠçŽ ã®æ¬ èœïŒ ããŒã¿ã«ã«ãŒããšããŠäœ¿çšããŠããDOMèŠçŽ ãã
createPortalã䜿çšããã³ã³ããŒãã³ããããŠã³ããããåã«ååšããããšã確èªããŠãã ãããè¯ãç¿æ £ã¯ããããçŽæ¥index.htmlã«é 眮ããããšã§ãã - Z-Indexã®ç«¶åïŒ
createPortalã§èŠçŽ ãé 眮ããéã«ã¯ãz-indexã®å€ã«æ³šæããŠãã ãããCSSã䜿çšããŠã¹ã¿ããã³ã°ã³ã³ããã¹ãã管çããããŒã¿ã«ã®ã³ã³ãã³ããæ£ãã衚瀺ãããããã«ããŸãã - ã€ãã³ããã³ããªã³ã°ã®åé¡ïŒ ã€ãã³ããããŒã¿ã«ãã©ã®ããã«äŒæããããçè§£ããé©åã«åŠçããŠãã ãããã€ãã³ããæå³ããªãã¢ã¯ã·ã§ã³ãããªã¬ãŒããã®ãé²ãããã«
e.stopPropagation()ã䜿çšããŸãã - ã¡ã¢ãªãªãŒã¯ïŒ ã¡ã¢ãªãªãŒã¯ãé¿ããããã«ã
createPortalã䜿çšããã³ã³ããŒãã³ããã¢ã³ããŠã³ãããããšãã«ãã€ãã³ããªã¹ããŒãšåç §ãé©åã«ã¯ãªãŒã³ã¢ããããŠãã ããããããå®çŸããã«ã¯ãã¯ãªãŒã³ã¢ãã颿°ãæã€useEffectããã¯ã䜿çšããŸãã - äºæãã¬ã¹ã¯ããŒã«åé¡ïŒ ããŒã¿ã«ãããŒãžã®æåŸ ãããã¹ã¯ããŒã«åäœã劚ããããšããããŸããã¹ã¿ã€ã«ãã¹ã¯ããŒã«ã劚ããŠããªãããšããŸãã¢ãŒãã«èŠçŽ ãééããéã«ããŒãžã®ãžã£ã³ããäºæãã¬ã¹ã¯ããŒã«åäœãåŒãèµ·ãããªãããšã確èªããŠãã ããã
ãŸãšã
React.createPortalã¯ãReactã§æè»ã§ã¢ã¯ã»ã·ãã«ããã€ä¿å®æ§ã®é«ãUIãäœæããããã®è²ŽéãªããŒã«ã§ãããã®ç®çãäœ¿çšæ¹æ³ãã€ãã³ããã³ããªã³ã°ãã¢ã¯ã»ã·ããªãã£ã®ããã®é«åºŠãªãã¯ããã¯ãçè§£ããããšã§ããã®åãæŽ»çšããŠãã°ããŒãã«ãªãªãŒãã£ãšã³ã¹ã«åªãããŠãŒã¶ãŒäœéšãæäŸããè€éã§é
åçãªWebã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ã§ããŸããã¢ããªã±ãŒã·ã§ã³ãå
æ¬çã§èª°ã«ã§ã䜿ããããã«ãåœéåãšã¢ã¯ã»ã·ããªãã£ã®ãã¹ããã©ã¯ãã£ã¹ãèæ
®ããããšãå¿ããªãã§ãã ããã
ãã®ã¬ã€ãã®ã¬ã€ãã©ã€ã³ãšäŸã«åŸãããšã§ãèªä¿¡ãæã£ãŠcreatePortalã䜿çšããŠäžè¬çãªUIã®èª²é¡ã解決ããçŽ æŽãããWebäœéšãåµé ããããšãã§ããŸãã